﻿using RestSharp;
using ServiceStack;
using ServiceStack.Text;
using ServiceStack.Web;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Reflection;
using DisconfClient;
using Ymatou.PerfMonitorClient;
using YmatouMessageBusClientNet4;
using YmtAuth.AppService.JavaService.Utils;
using YmtAuth.Domain;
using YmtSystem.CrossCutting;
using JsonObject = ServiceStack.Text.JsonObject;
using Newtonsoft.Json;

namespace YmtAuth.AppService.JavaService
{
    public class RequestUserService
    {
        private static readonly string busAppId = "juserservice";
        private static Dictionary<String, String> sendToBusDictionary;

       
        static RequestUserService()
        {
            var cfg = ConfigurationManager.AppSettings["auth_bus_code"].Split(new char[] { ',' });
            sendToBusDictionary = cfg.ToDictionary(c => c.Split(';')[0], c => c.Split(';')[1]);
        }

        /// <summary>
        /// 是否关闭双写（只写JAVA）
        /// </summary>
        /// <returns></returns>
        private static bool getTransferWriteRequestToJavaServiceSwitch()
        {
            return ConfigManager.GetConfigValue("TransferWriteRequestToJavaServiceSwitch", "0") == "1";
        }

        /// <summary>
        /// 关闭双写（只写JAVA）
        /// </summary>
        /// <param name="reqObj"></param>
        /// <param name="reqDtoFullName"></param>
        /// <param name="reqPath"></param>
        /// <param name="httpMethod"></param>
        /// <returns></returns>
        public static object transferWriteRequestToJavaService(Object reqObj, string reqDtoFullName, string reqPath, string httpMethod)
        {
            //仍双写
            if (!getTransferWriteRequestToJavaServiceSwitch())
            {
                return null;
            }

            var requestURL = getPostUrl(reqDtoFullName, httpMethod);

            //非“写”接口
            if (string.IsNullOrEmpty(requestURL))
            {
                return null;
            }

            requestURL = string.Concat(requestURL, "Dto");
            var requestId = getRequestId(reqObj);


            try
            {
                var client = new RestClient(requestURL);
                var restRequest = new RestRequest();

                restRequest.Method = Method.POST;
                restRequest.AddHeader("Accept", "application/json");
                restRequest.Parameters.Clear();
                restRequest.AddParameter("application/json", JsonConvert.SerializeObject(reqObj), ParameterType.RequestBody);

                var resp = client.Execute(restRequest);
                resp.CheckResponse();
                var respObj = (Object)resp.Content;
                YmatouLoggingService.Info("【关闭双写请求】, reqPath: {0}, data:{1}，发送到java service，返回:{2}", reqPath, reqObj.ToJson(), respObj);

                return respObj;
            }
            catch (Exception ex)
            {
                YmatouLoggingService.Error(string.Format("【关闭双写请求】处理失败, reqPath: {0}, data:{1}", reqPath, reqObj.ToJson()), ex);
                using (MethodMonitor.New("AUTH_OnlyJAVAWrite_Exception"));
                throw;
            }
        }

        public static void sendWriteRequest(Object reqObj, Object respObj, string reqDtoFullName, string reqPath, string httpMethod)
        {
            var code = getCode(reqDtoFullName, httpMethod);
            if (code.IsEmpty())
            {
                YmatouLoggingService.Info("DTO:{0},httpmethod:{1},未配置总线CODE,不发消息", reqDtoFullName, httpMethod);
                return;
            }
            if (!getRespResult(respObj, "Success", false))
            {
                YmatouLoggingService.Info("请求 {0}，业务处理失败,code:{1},respMsg:{2}，不发消息到总线"
                    , reqPath, getRespResult(respObj, "ResponseCode", 500), getRespResult(respObj, "LastErrorMessage", ""));
                return;
            }
            //设置请求userid（WeiXinUserUnionYmatouUserRequestDto，WeiXinUserRequestDto，AliUserRequestDto，UserRegisterInfoRequestDto）
            setRegUserId(reqObj,respObj,reqDtoFullName);
            //返回的报文
            ////第三方用户设置登录loginId
            //String loginId=null;
            //if (reqDtoFullName.ToLower() == "sinaweibouserrequestdto"
            //    || reqDtoFullName.ToLower() == "weixinuserrequestdto"
            //    || reqDtoFullName.ToLower() == "aliuserrequestdto")
            //{
            //    var respUserId = getRespUserId(respObj);
            //    if (respUserId > 0)
            //    {
            //        try
            //        {
            //            loginId = YmtAuth.Repository.MSSQL.RepositoryDependencies.UserRepository.Invoke().FindUserLoginId(respUserId);
            //            setRegLoginId(reqObj, loginId, reqDtoFullName);
            //        }
            //        catch (Exception ex)
            //        {
            //            YmatouLoggingService.Error("Userservice 双写设置loginId失败,",ex);
            //        }
            //    }
            //}
            var requestId = getRequestId(reqObj);            
            int tempUserId = 0;
            if (!string.Equals(reqDtoFullName, "lockuserrequestdto", StringComparison.InvariantCultureIgnoreCase) && !string.Equals(reqDtoFullName, "unlockuserrequestdto", StringComparison.InvariantCultureIgnoreCase))
            {
                tempUserId = getUserId(reqObj);
            }

            var insetResult = YmtAuth.Repository.MSSQL.RepositoryDependencies.UserOpSyncRepo.Invoke().Insert(new UserOperationSyncEntity
            {
                userId = tempUserId,
                opContent = reqObj.ToJson(),
                loginId = getLoginId(reqObj),
                opId = requestId,
                opType = code,
                requestURL = getPostUrl(reqDtoFullName, httpMethod),
                synced = SyncStatus.NoSync
            }, true);

            var sendResult = MessageBusAgent.PublishSync(busAppId, code, requestId, reqObj);

            YmatouLoggingService.Debug("请求 {0}，业务处理成功,发送消息到总线,{1},消息状态写入DB:{2}", reqPath, sendResult, insetResult);
            using (MethodMonitor.New("AUTH_DoubleWrite_OK"));
        }

        public static SendReadToJavaUserServiceResp<T> sendReadRequest<T>(Object reqObj, string reqDtoFullName, string reqPath, string httpMethod, INameValueCollection queryString)
        {
            var reqCfg = getSendReadToJavaUserServiceReqCfg(reqDtoFullName, httpMethod);
            if (reqCfg == null)
            {
                return new SendReadToJavaUserServiceResp<T> { isRequestSuccess = false };
            }
            try
            {

                if (reqCfg.httpMethod == "GET")
                {
                    var client = new RestClient(reqCfg.url);
                    var restRequest = new RestRequest(Method.GET);
                    foreach (var key in queryString)
                    {
                        restRequest.AddParameter(key.ToString(), queryString.Get(key.ToString()));
                    }
                    var resp = client.Execute(restRequest);
                    resp.CheckResponse();
                    var respObj = JsonConvert.DeserializeObject<T>(resp.Content);
                    YmatouLoggingService.Info("请求 {0}，data:{1}，发送到java userservice，返回:{2}", reqPath, reqObj.ToJson(), resp.Content);
                    return new SendReadToJavaUserServiceResp<T> { isRequestSuccess = true, resp = respObj };
                }
                else if (reqCfg.httpMethod == "POST")
                {
                    var client = new RestClient(reqCfg.url);
                    var restRequest = new RestRequest();

                    restRequest.Method = Method.POST;
                    restRequest.AddHeader("Accept", "application/json");
                    restRequest.Parameters.Clear();
                    restRequest.AddParameter("application/json", JsonConvert.SerializeObject(reqObj), ParameterType.RequestBody);

                    var resp = client.Execute(restRequest);
                    resp.CheckResponse();
                    var respObj = JsonConvert.DeserializeObject<T>(resp.Content);
                    YmatouLoggingService.Info("请求 {0},data:{1}，发送到java userservice，返回:{2}", reqPath, reqObj.ToJson(), resp.Content);
                    return new SendReadToJavaUserServiceResp<T> { isRequestSuccess = true, resp = respObj };
                }
                else
                    throw new NotSupportedException("not supper {0}".Fmt(reqCfg.httpMethod));
            }
            catch (Exception ex)
            {
                YmatouLoggingService.Error("请求JAVAUserService error,", ex);
                using (MethodMonitor.New("UserService_DoubleReader_Fail")) ;
                return new SendReadToJavaUserServiceResp<T> { isRequestSuccess = false };
            }
        }

        //检查是否分流读比例
        public static bool isSendReadToJavaUserService(Object reqObj, string reqDtoFullName, string httpMethod, IEnumerable<string> requestKey)
        {
            //检查是否开启双读总开关
            if (!getSendReadToJavaUserServiceMasterSwitch())
            {
                return false;
            }
            //如果没有配置双读URL，则返回false，不需要分流
            if (getGetUrl(reqDtoFullName, httpMethod).IsEmpty())
            {
                return false;
            }
            //按请求关键字匹配白名单
            if (requestKey != null && requestKey.Any())
            {
                var wList = ConfigManager.GetConfigValue("UserInterfaceWhiteList", string.Empty).Split(new char[] { ';' });
                if (wList != null && wList.Length > 0 && wList.Any(w => requestKey.Any(r => r.EqualsIgnoreCase(w))))
                {
                    YmatouLoggingService.Debug("接口 {0} httpMethod:{1},requestKey:{2} 请求匹配白名单，倒流到java用户服务",
                        reqDtoFullName, httpMethod,
                        requestKey);
                    return true;
                }
            }
            //判断分流的类型，按比例 或者 按用户分
            var flowArray = getSendReadToJavaUserServiceFlowType(reqDtoFullName, httpMethod);
            //按用户分流
            var userId = getUserId(reqObj).ToString();
            if (!userId.IsEmpty() && userId != "0" && flowArray.Any(f => f != "0" && f.EqualsIgnoreCase(userId)))
            {
                YmatouLoggingService.Debug("请求 {0},{1},按用户userId [{2}]分流,是否分流到JAVA UserService 读请求 {3}", reqDtoFullName,
                    httpMethod, userId, true);
                return true;
            }
            //按loginId 分流
            var loginId = getLoginId(reqObj);
            if (!loginId.IsEmpty() && flowArray.Any(f => f != "0" && f.EqualsIgnoreCase(loginId)))
            {
                YmatouLoggingService.Debug("请求 {0},{1},按用户LoginId [{2}] 分流,是否分流到JAVA UserService 读请求 {3}",
                    reqDtoFullName, httpMethod, loginId, true);
                return true;
            }
            //按比例分流，0:不分流，10:全分流
            var isFlow = Math.Abs(reqObj.ToId().GetHashCode()) % 10 + 1 <= Convert.ToInt32(flowArray[0]);
            YmatouLoggingService.Debug("请求 {0},{1},按比例分流,是否分流到JAVA UserService 读请求 {2}", reqDtoFullName, httpMethod,
                isFlow);
            return isFlow;
        }

        private static string ParametersEncoding(string val)
        {
            return string.IsNullOrEmpty(val) ? val : System.Web.HttpUtility.UrlEncode(val);
        }

        private static String[] getSendReadToJavaUserServiceFlowType(string reqDtoFullName, string httpMethod)
        {
            var key = "{0}_{1}".Fmt(reqDtoFullName, httpMethod).ToLower();
            //获取具体接口配置的分流比例
            var cfgVal = ConfigManager.GetConfigValue<string>(key);
            if (cfgVal.IsEmpty())
            {
                //如果没有配置具体接口的流量比例，则获取默认的流量比例
                cfgVal = ConfigManager.GetConfigValue("Default_SendReadTo_JavaUserService_Flow", "0");
            }
            return cfgVal.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
        }
        private static bool getSendReadToJavaUserServiceMasterSwitch()
        {
            return ConfigManager.GetConfigValue("SendReadToJavaUserServiceMasterSwitch", "0") == "1";
        }

        private static string getGetUrl(String reqObjName, string httpMethod)
        {
            var cfg = getSendReadToJavaUserServiceReqCfg(reqObjName, httpMethod);
            return cfg == null ? null : cfg.url;
        }

        private static SendReadToJavaUserServiceReq getSendReadToJavaUserServiceReqCfg(String reqObjName, string httpMethod)
        {
            var key = String.Format("{0}_{1}", reqObjName, httpMethod).ToLower();
            return RequestCfg.getReaderDictionary.ContainsKey(key) ? RequestCfg.getReaderDictionary[key] : null;
        }

        private static string getCode(String reqObjName, string httpMethod)
        {
            var key = "{0}_{1}".Fmt(reqObjName, httpMethod).ToLower();
            return sendToBusDictionary.ContainsKey(key) ? sendToBusDictionary[key] : null;
        }

        private static string getPostUrl(String reqObjName, string httpMethod)
        {
            var key = "{0}_{1}".Fmt(reqObjName, httpMethod).ToLower();
            return RequestCfg.postWreteUrlDic.ContainsKey(key) ? RequestCfg.postWreteUrlDic[key] : null;
        }

        private static string getRequestId(Object reqObj)
        {
            var method= reqObj.GetType()
                .GetProperty("RequestId", BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
            return method != null ? method.GetValue(reqObj) as string : null;
        }

        private static int getUserId(Object reqObj)
        {
            var propertyInfos = reqObj.GetType().GetProperties(BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
            var propertyInfo = propertyInfos.FirstOrDefault(p => p.Name.EqualsIgnoreCase("iUserId") || p.Name.EqualsIgnoreCase("UserId"));

            if (propertyInfo != null && propertyInfo.CanRead)
            {
                return Convert.ToInt32(propertyInfo.GetValue(reqObj));
            }
            else
            {
                return 0;
            }
        }

        private static string getLoginId(Object reqObj)
        {
            var propertyInfos = reqObj.GetType().GetProperties(BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
            var propertyInfo = propertyInfos.FirstOrDefault(p => p.Name.EqualsIgnoreCase("loginId") || p.Name.EqualsIgnoreCase("sLoginId"));

            if (propertyInfo != null && propertyInfo.CanRead)
            {
                return propertyInfo.GetValue(reqObj) as string;
            }
            else
            {
                return null;
            }
        }

        private static TReuslt getRespResult<TReuslt>(Object respObj, String key, TReuslt defReuslt = default(TReuslt))
        {
            var obj = JsonObject.Parse(respObj.ToJson());
            if (obj.ContainsKey(key))
            {
                return obj.Get<TReuslt>(key);
            }
            return defReuslt;
        }

        private static void setRegUserId(Object reqObj, Object respObj, string reqObjName)
        {
            var objName = reqObjName.ToLower();
            switch (objName)
            {
                    //码头用户注册
                case "userregisterinforequestdto":
                    setReqProperty(reqObj, respObj);
                    break;
                case "sinaweibouserrequestdto":
                    setReqProperty(reqObj, respObj);
                    break;
                case "aliuserrequestdto":
                    setReqProperty(reqObj, respObj);
                    break;
                case "weixinuserrequestdto":
                    setReqProperty(reqObj, respObj);
                    break;
            }           
        }
        private static void setRegLoginId(Object reqObj, string loginId, string reqObjName)
        {
            var objName = reqObjName.ToLower();
            switch (objName)
            {               
                case "sinaweibouserrequestdto":
                    setReqProperty(reqObj, "sName", loginId);
                    break;
                case "aliuserrequestdto":
                    setReqProperty(reqObj, "sRealName", loginId);
                    break;
                case "weixinuserrequestdto":
                    setReqProperty(reqObj, "Name", loginId);
                    break;
            }
        }

        private static void setReqProperty(object reqObj, string setPropertyName, object val)
        {
            var property = reqObj.GetType()
              .GetProperty(setPropertyName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
            if (property != null && property.CanWrite)
            {
                property.SetValue(reqObj,val);
            }
        }

        private static void setReqProperty(object reqObj, object repObj,string respResultName="Result",String respUserIdName="UserId",string reqUserIdName="UserId")
        {
            var property = reqObj.GetType()
                .GetProperty(reqUserIdName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
            if (property != null && property.CanWrite)
            {
                var childJson = JsonObject.Parse(repObj.ToJson()).Child(respResultName);
                if (!childJson.IsEmpty())
                {
                    var uId = JsonObject.Parse(childJson).Get<int>(respUserIdName);
                    property.SetValue(reqObj, uId);
                }
            }
        }

        private static int getRespUserId( object repObj,string respResultName="Result",String respUserIdName="UserId",string reqUserIdName="UserId")
        {
            
            var childJson = JsonObject.Parse(repObj.ToJson()).Child(respResultName);
            if (!childJson.IsEmpty())
            {
                var uId = JsonObject.Parse(childJson).Get<int>(respUserIdName);
                return uId;
            }
            return 0;
        }
    }
}
